package de.mizi.controller;

import de.mizi.tilemap.creature.Player;
import de.mizi.tilemap.factory.TilemapObjectFactory;
import de.mizi.tilemap.object.LocatableTilemapObject;
import de.mizi.tilemap.object.TilemapObjectDrawData;
import de.mizi.tilemap.tile.Tile;

/**
 * Holds the data of the map, the player, the items and so on.
 * This class becomes serializable in the near future.
 * @author mizi
 *
 */
public class GameData extends GameModel {
	private TilemapObjectFactory factory;
	private Tile[][] mapLayout;
	private Player player;
	private LocatableTilemapObject cursor;
	
	private static final int w = TilemapObjectFactory.WALL_TILE;
	private static final int f = TilemapObjectFactory.FLOOR_TILE;
	private int[][] MAP_DATA = { {w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w},
                                 {w,f,f,f,f,f,f,f,f,f,f,f,f,f,f,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,f,f,f,f,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w},
                                 {w,f,f,f,f,f,f,f,f,f,f,f,f,f,f,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,f,f,f,f,w,w,w,w,w,w,w,w,w,w,f,f,f,f,f,f,w},
                                 {w,f,f,f,f,f,f,f,f,f,f,f,f,f,f,w,w,w,w,w,w,w,w,w,w,f,f,f,f,f,w,w,w,f,f,f,f,w,w,w,w,w,w,w,w,w,w,f,f,f,f,f,f,w},
                                 {w,f,f,f,f,f,f,f,f,f,f,f,f,f,f,w,w,w,w,w,w,w,w,w,w,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,w,w,w,w,f,f,f,f,f,f,w},
                                 {w,f,f,f,f,f,f,f,f,f,f,f,f,f,f,w,w,w,w,w,w,w,w,w,w,f,f,f,f,f,w,w,w,f,f,f,f,w,w,w,w,w,f,w,w,w,w,w,w,f,w,w,w,w},
                                 {w,w,w,w,w,w,w,f,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,f,f,f,f,f,f,f,w,w,w,f,f,f,f,w,w,w,f,f,f,f,f,w,w,w,w,f,w,w,w,w},
                                 {w,w,w,w,w,w,w,f,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,f,w,f,f,f,f,f,w,w,w,f,f,f,f,w,w,w,f,f,f,f,f,f,f,f,f,f,w,w,w,w},
                                 {w,w,w,w,w,w,w,f,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,f,w,f,f,f,f,f,w,w,w,w,w,w,w,w,w,w,f,f,f,f,f,w,w,w,w,w,w,w,w,w},
                                 {w,w,w,w,w,w,w,f,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,f,w,f,f,f,f,f,w,w,w,w,w,w,w,w,w,w,f,f,f,f,f,w,w,w,w,w,w,w,w,w},
                                 {w,w,w,w,w,w,w,f,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,f,w,f,f,f,f,f,w,w,w,w,w,w,w,w,w,w,f,f,f,f,f,w,w,w,w,w,w,w,w,w},
                                 {w,w,w,w,w,w,w,f,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,f,w,w,w,w,f,w,w,w,w,w,w,w,w,w,w,w,f,f,f,f,f,f,f,f,f,f,f,w,w,w},
                                 {w,w,w,w,w,w,w,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,w,w,w,w,f,w,w,w,w,w,w,w,w,w,w,w,f,f,f,f,f,w,w,w,w,w,f,w,w,w},
                                 {w,w,w,w,w,w,w,w,w,w,w,w,w,w,f,w,w,w,w,w,w,w,w,w,w,w,w,w,f,w,w,w,w,w,w,w,w,w,w,w,f,f,f,f,f,w,w,f,f,f,f,f,f,w},
                                 {w,w,w,w,w,w,w,w,w,w,w,w,w,w,f,w,w,w,w,w,w,w,w,w,w,w,w,w,f,w,w,w,w,w,w,w,w,w,w,w,f,f,f,f,f,w,w,f,f,f,f,f,f,w},
                                 {w,w,w,w,w,w,w,w,w,w,w,w,w,w,f,w,w,w,w,w,w,w,w,w,w,w,w,w,f,w,w,w,w,w,w,w,w,w,w,w,f,f,f,f,f,w,w,f,f,f,f,f,f,w},
                                 {w,w,w,w,w,w,w,w,w,w,w,w,w,w,f,w,w,w,w,w,w,w,w,w,w,w,w,w,f,w,w,w,w,w,w,w,w,w,w,w,f,f,f,f,f,w,w,f,f,f,f,f,f,w},
                                 {w,w,w,w,w,w,w,w,w,w,w,w,w,w,f,w,w,w,w,w,w,w,w,w,w,w,w,w,f,w,w,w,w,w,w,w,w,w,w,w,f,f,f,f,f,w,w,f,f,f,f,f,f,w},
                                 {w,w,w,w,w,w,w,w,w,w,w,w,w,w,f,w,w,w,w,w,w,w,w,w,w,w,w,f,f,f,f,f,f,f,w,w,w,w,w,w,f,f,f,f,f,w,w,f,f,f,f,f,f,w},
                                 {w,w,w,w,w,w,w,w,w,w,w,w,w,w,f,w,w,w,w,w,w,w,w,w,w,w,w,f,f,f,f,f,f,f,w,w,w,w,w,w,f,f,f,f,f,w,w,f,f,f,f,f,f,w},
                                 {w,w,w,w,w,w,w,w,w,w,w,f,f,f,f,f,f,f,f,f,f,f,f,w,w,w,w,f,f,f,f,f,f,f,w,w,w,w,w,w,f,f,f,f,f,w,w,f,f,f,f,f,f,w},
                                 {w,w,w,w,w,w,w,w,w,w,w,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,w,w,w,w,w,w,f,f,f,f,f,w,w,w,w,w,w,w,w,w},
                                 {w,w,w,w,w,w,w,w,w,w,w,f,f,f,f,f,f,f,f,f,f,f,f,w,w,w,w,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,w,w,w,w,w,w,w,w,w},
                                 {w,w,w,w,w,w,w,w,w,w,w,f,f,f,f,f,f,f,f,f,f,f,f,w,w,w,w,f,f,f,f,f,f,f,w,w,w,w,w,w,f,f,f,f,f,w,w,w,w,w,w,w,w,w},
                                 {w,w,w,w,w,w,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,w,w,w,w,f,f,f,f,f,f,f,w,w,w,w,w,w,f,f,f,f,f,w,w,w,w,w,w,w,w,w},
                                 {w,w,w,w,w,w,f,w,w,w,w,f,f,f,f,f,f,f,f,f,f,f,f,w,w,w,w,f,f,f,f,f,f,f,w,w,w,w,w,w,f,f,f,f,f,w,w,f,f,f,f,f,w,w},
                                 {w,w,w,w,w,w,f,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,f,f,f,f,f,f,f,w,w,w,w,w,w,f,f,f,f,f,w,w,f,f,f,f,f,w,w},
                                 {w,w,w,w,w,w,f,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,f,w,w,w,w,w,w,w,w,f,f,f,f,f,w,w,f,f,f,f,f,w,w},
                                 {w,w,w,w,w,w,f,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,f,w,w,w,w,w,w,w,w,f,f,f,f,f,f,f,f,f,f,f,f,w,w},
                                 {w,w,w,w,w,w,f,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,f,w,w,w,w,w,w,w,w,f,f,f,f,f,w,w,f,f,f,f,f,w,w},
                                 {w,w,w,w,w,w,f,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,f,w,w,w,w,w,w,w,w,f,f,f,f,f,w,w,f,f,f,f,f,w,w},
                                 {w,w,w,w,w,w,f,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,f,w,w,w,w,w,w,w,w,f,f,f,f,f,w,w,f,f,f,f,f,w,w},
                                 {w,w,w,w,w,f,f,f,f,f,f,f,f,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,f,f,f,f,f,f,f,f,f,f,f,f,f,f,w,w,f,f,f,f,f,w,w},
                                 {w,w,w,w,w,f,f,f,f,f,f,f,f,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,f,w,w,w,w,w,w,w,w,f,f,f,f,f,w,w,f,f,f,f,f,w,w},
                                 {w,w,w,w,w,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,w,w,w,w,w,w,w,w,f,f,f,f,f,w,w,f,f,f,f,f,w,w},
                                 {w,w,w,w,w,f,f,f,f,f,f,f,f,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,f,f,f,f,f,w,w,f,f,f,f,f,w,w},
                                 {w,w,w,w,w,f,f,f,f,f,f,f,f,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,f,f,f,f,f,w,w},
                                 {w,w,w,w,w,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,f,f,f,f,f,w,w},
                                 {w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,w,w},
                                 {w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w,w}};
	
	/**
	 * Constructs a new GameData object using the given factory to create
	 * necessary objects.
	 * @param factory the factory
	 */
	public GameData(TilemapObjectFactory factory) {
		this.factory = factory;
	}
	
	/**
	 * Creates a new Player object at the given position.
	 * @param x the x position
	 * @param y the y position
	 */
	public void createNewPlayer(int x, int y) {
		player = factory.createPlayer(x, y);
		GameEvent newPlayerData = new GameEvent(GameEventType.NEW_PLAYER, (LocatableTilemapObject)player);
		GameEvent newMessage = new GameEvent(String.format("Neuer Spieler bei x: %d | y: %d kreiert.", player.getX(), player.getY()));
		for(GameListener listener: listeners) {
			listener.update(newPlayerData);
			listener.update(newMessage);
		}
	}
	
	/**
	 * Set the position of the player to a new one.
	 * @param x the new x position
	 * @param y the new y position
	 */
	public void setPlayerPosition(int x, int y) {
		player.setX(x);
		player.setY(y);
		GameEvent newPlayerPosition = new GameEvent(GameEventType.PLAYER_POSITION_CHANGED, player.getX(), player.getY());
		for(GameListener listener: listeners) {
			listener.update(newPlayerPosition);
		}
	}
	
	/**
	 * Create a new cursor.
	 * @param x the x position
	 * @param y the y position
	 */
	public void createNewCursor(int x, int y) {
		cursor = factory.createSpecialObject(TilemapObjectFactory.CURSOR_OBJECT, x, y);
		GameEvent newCursorData = new GameEvent(GameEventType.NEW_CURSOR, cursor);
		for(GameListener listener: listeners) {
			listener.update(newCursorData);
		}
	}
	
	/**
	 * Set the cursor position to a new one.
	 * @param x the new x position
	 * @param y the new y position
	 */
	public void setCursorPosition(int x, int y) {
		cursor.setX(x);
		cursor.setY(y);
		String description = mapLayout[y][x].getDescription();
		if(player.getX() == x && player.getY() == y) {
			description = player.getDescription();
		}
		GameEvent newCursorPosition = new GameEvent(GameEventType.CURSOR_POSITION_CHANGED, cursor.getX(), cursor.getY());
		GameEvent newMessage = new GameEvent(description);
		for(GameListener listener: listeners) {
			listener.update(newCursorPosition);
			listener.update(newMessage);
		}
	}
	
	/**
	 * Set the visibility of the cursor.
	 * @param showCursor true shows the cursor, false doesn't
	 */
	public void setShowCursor(boolean showCursor) {
		GameEvent newShowCursorEvent = new GameEvent(GameEventType.SET_SHOW_CURSOR, showCursor);
		for(GameListener listener: listeners) {
			listener.update(newShowCursorEvent);
		}
	}
	
	/**
	 * Returns true if the tile at the given position is walkable, false
	 * otherwise.
	 * @param x the x position
	 * @param y the y position
	 * @return true if the tile is walkable, false otherwise
	 */
	public boolean isWalkableAtPosition(int x, int y) {
		// If the tile is not blocking and there is no player.
		boolean isPlayerPosition = player.getX() == x && player.getY() == y;
		boolean isBlockingTile = mapLayout[y][x].isBlocking();
		return !isPlayerPosition && !isBlockingTile;
	}
	
	// TODO: This is just to have something to play with.
	/**
	 * Creates a new map.
	 */
	public void createNewMap() {
		mapLayout = new Tile[MAP_DATA.length][MAP_DATA[0].length];
		for(int row = 0; row < MAP_DATA.length; ++row) {
			for(int column = 0; column < MAP_DATA[row].length; ++column) {
				mapLayout[row][column] = factory.createTile(MAP_DATA[row][column]);
			}
		}
		
		TilemapObjectDrawData[][] drawData = new TilemapObjectDrawData[mapLayout.length][mapLayout[0].length];
		for(int row = 0; row < mapLayout.length; ++row) {
			for(int column = 0; column < mapLayout[row].length; ++column) {
				drawData[row][column] = mapLayout[row][column].getDrawData();
			}
		}
		
		GameEvent newMap = new GameEvent(drawData);
		for(GameListener listener: listeners) {
			listener.update(newMap);
		}
	}
	
	/**
	 * Get the height of the map in tiles.
	 * @return the height in tiles
	 */
	public int getMapHeightInTiles() {
		return MAP_DATA.length;
	}
	
	/**
	 * Get the width of the map in tiles.
	 * @return the width in tiles
	 */
	public int getMapWidthInTiles() {
		return MAP_DATA[0].length;
	}
}
